package soot.we.android.component;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import soot.Body;
import soot.MethodOrMethodContext;
import soot.Pack;
import soot.PackManager;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.Transform;
import soot.Unit;
import soot.UnitBox;
import soot.Value;
import soot.ValueBox;
import soot.jimple.IntConstant;
import soot.jimple.InvokeExpr;
import soot.jimple.Stmt;
import soot.toolkits.graph.ExceptionalUnitGraph;
import soot.toolkits.graph.UnitGraph;
import soot.we.android.XML.EntityApplicationBase;
import soot.we.android.callGraph.BuildCallGraph;

public class ProcessJimpleClass {

	public static List<EntityClass> classCfgList = new ArrayList<EntityClass>();
	private static Set<String> androidCallbacks = new HashSet<String>();
	private static Map<SootClass, Set<Integer>> layoutClasses = new HashMap<SootClass, Set<Integer>>();

	public static Set<String> getAndroidCallbacks() {
		return androidCallbacks;
	}

	public static void setAndroidCallbacks(Set<String> androidCallbacks) {
		ProcessJimpleClass.androidCallbacks = androidCallbacks;
	}

	public void run() throws IOException {

		androidCallbacks = loadAndroidCallbacks();

		SootConfig sc = new SootConfig();
		String[] soot_args = sc.intializeSoot();
		Pack pack1 = PackManager.v().getPack("wjtp");
		pack1.add(new Transform("wjtp.myTrans1", new MySceneTransformer()));
		soot.Main.main(soot_args);

		for (EntityClass ec : classCfgList) {
			System.out.println("..." + ec.getCompName());
		}

		BuildCallGraph buildCallGraph = new BuildCallGraph(classCfgList);
		buildCallGraph.getBuildCallGraph();

	}

	public static UnitGraph getCfgFromMethod(Body body) {
		ArrayList<String> unitList = new ArrayList<String>();
		SootMethod method = body.getMethod();
		System.out.println("instrumenting method : " + method.getName());
		UnitGraph g = new ExceptionalUnitGraph(body);

		return g;
	}

	public void searchInvoke(Body body) {
		// **********find Static invoke***************
		Iterator<Unit> iter = body.getUnits().snapshotIterator();
		while (iter.hasNext()) {
			Stmt s = (Stmt) iter.next();
			if (s.containsInvokeExpr()) {
				String declaringClass = s.getInvokeExpr().getMethod()
						.getDeclaringClass().getName();
				String name = s.getInvokeExpr().getMethod().getName();

				System.out.println("declaringClass: " + declaringClass);
				System.out.println("name: " + name);

				if (declaringClass.equals("android.content.Intent")) {
					for (ValueBox ss : s.getUseBoxes()) {
						System.out.println("declaringClass's values : "
								+ ss.getValue().toString());
					}
				}
			}
		}
	}

	public static List<String> findAnonyInnerClass(String Outerclassname) {
		List<String> outClassList = new ArrayList<String>();
		Iterator<SootClass> iterator = Scene.v().getApplicationClasses()
				.iterator();
		while (iterator.hasNext()) {
			String classname = iterator.next().getName();
			if (classname.contains(Outerclassname)
					&& !classname.equals(Outerclassname)) {
				System.out.println("Anonyinnerclass :" + classname);
				outClassList.add(classname);
			}
		}
		return outClassList;

	}
	/**
	 * Loads the set of interfaces that are used to implement Android callback
	 * handlers from a file on disk
	 * @return A set containing the names of the interfaces that are used to
	 * implement Android callback handlers
	 */
	private Set<String> loadAndroidCallbacks() throws IOException {
		Set<String> androidCallbacks = new HashSet<String>();
		BufferedReader rdr = null;
		try {
			rdr = new BufferedReader(new FileReader("AndroidCallbacks.txt"));
			String line;
			while ((line = rdr.readLine()) != null)
				if (!line.isEmpty())
					androidCallbacks.add(line);
		}
		finally {
			if (rdr != null)
				rdr.close();
		}
		return androidCallbacks;
	}

	/**
	 * Finds the mappings between classes and their respective layout files
	 */
	public  static void findClassLayoutMappings() {
		Iterator<MethodOrMethodContext> rmIterator = Scene.v().getReachableMethods().listener();
		while (rmIterator.hasNext()) {
			SootMethod sm = rmIterator.next().method();
			if (!sm.isConcrete())
				continue;
			for (Unit u : sm.retrieveActiveBody().getUnits())
				if (u instanceof Stmt) {
					Stmt stmt = (Stmt) u;
					if (stmt.containsInvokeExpr()) {
						InvokeExpr inv = stmt.getInvokeExpr();
						if (inv.getMethod().getName().equals("setContentView")
								&& inv.getMethod().getDeclaringClass().getName().equals("android.app.Activity")) {
							for (Value val : inv.getArgs())
								if (val instanceof IntConstant) {
									IntConstant constVal = (IntConstant) val;
									if (layoutClasses.containsKey(sm.getDeclaringClass()))
										layoutClasses.get(sm.getDeclaringClass()).add(constVal.value);
									else {
										Set<Integer> layoutIDs = new HashSet<Integer>();
										layoutIDs.add(constVal.value);
										layoutClasses.put(sm.getDeclaringClass(), layoutIDs);
									}
								}
						}
					}
				}
		}
	}
}
